package main

import (
	"fmt"
)

/*

类型指针，允许对这个指针类型的数据进行修改，传递数据可以直接使用指针，而无须拷贝数据，类型指针不能进行偏移和运算。
切片，由指向起始元素的原始指针、元素数量和容量组成。

受益于这样的约束和拆分，Go语言的指针类型变量即拥有指针高效访问的特点，又不会发生指针偏移，从而避免了非法修改关键性数据的问题。同时，垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。

切片比原始指针具备更强大的特性，而且更为安全。切片在发生越界时，运行时会报出宕机，并打出堆栈，而原始指针只会崩溃。

一个指针变量可以指向任何一个值的内存地址，它所指向的值的内存地址在32和64位机器上分别占用4或8个字节，占用字节的大小与所指向的值的大小无关。当一个指针被定义后没有分配到任何变量时，它的默认值为nil。指针变量通常缩写为ptr。

当使用&操作符对普通变量进行取地址操作并得到变量的指针后，可以对指针使用*操作符，也就是指针取值。

取地址操作符&和取值操作符*是一对互补操作符，&取出地址，*根据地址取出地址指向的值。

变量、指针地址、指针变量、取地址、取值的相互关系和特性如下：
对变量进行取地址操作使用&操作符，可以获得这个变量的指针变量。
指针变量的值是指针地址。
对指针变量进行取值操作使用*操作符，可以获得指针变量指向的原变量的值。

*/

func main() {

	// 基础示例
	var num int = 10
	var ptr *int = &num
	fmt.Println(&num)
	fmt.Println(ptr)
	fmt.Println(&ptr)
	fmt.Println(*ptr)

	*ptr = 20
	fmt.Println(num)

	var cat int = 1
	var str string = "banana"
	// 使用%p输出指针的地址
	fmt.Printf("%p %p\n", &cat, &str)
	// 使用%T获取指针的类型
	fmt.Printf("%T %T\n", &cat, &str)

	p1 := &cat
	p2 := &str

	fmt.Printf("%d %s\n", *p1, *p2)

	// 修改指针的值
	a, b := 1, 2
	swap(&a, &b)
	fmt.Println(a, b)

	aa, bb := 2, 3
	fmt.Println(aa & bb)
	fmt.Println(aa | bb)
	aa = -2
	fmt.Println(aa & bb)
	fmt.Println(aa | bb)
}

// 交换函数
func swap(a, b *int) {
	// 取a指针的值, 赋给临时变量t
	t := *a
	// 取b指针的值, 赋给a指针指向的变量
	*a = *b
	// 将a指针的值赋给b指针指向的变量
	*b = t
}

// 交换函数
func swap2(a, b *int) {
	b, a = a, b
}
